using System;
using System.Collections.Generic;
using System.Linq;
using System.Text.RegularExpressions;
using System.Xml;
using System.Xml.Linq;
using HQLAddin.Highlighting.Classificators;
using Microsoft.VisualStudio.Text;

namespace HQLAddin.Highlighting
{
	public static class SnapshotExtensions
	{
		public static string GetQueryNameAtLine(this ITextSnapshot snapshot, int line)
		{
			var document = XDocument.Parse(string.Join(Environment.NewLine, snapshot.Lines.Select(l => l.GetText())),LoadOptions.SetLineInfo);
			var sample = document.Descendants().OrderBy(x => ((IXmlLineInfo) x).LineNumber).LastOrDefault(x => ((IXmlLineInfo) x).LineNumber -1 <= line);
			return sample != null ? sample.Attributes().Where(a => a.Name.LocalName == "name").First().Value : string.Empty;
		}

		public static string GetQueryAtLine(this ITextSnapshot snapshot, int line)
		{
			return GetQueryInfoAtLine(snapshot, line).Query;
		}

		public static QueryInfo GetQueryInfoAtLine(this ITextSnapshot snapshot, int line)
		{
			try
			{
				var document = XDocument.Parse(string.Join(Environment.NewLine, snapshot.Lines.Select(l => l.GetText())), LoadOptions.SetLineInfo );
				var sample = document.Descendants().OrderBy(x => ((IXmlLineInfo)x).LineNumber).LastOrDefault(x => ((IXmlLineInfo)x).LineNumber - 1 <= line);

				if (sample != null && sample.Name.LocalName == "query")
				{
					var queryLines = sample.Value.Split('\n')
											.SkipWhile(l => string.IsNullOrEmpty(l.Trim())); //this will remove empty lines at the begining
					var query = string.Join(Environment.NewLine, queryLines);
					return new QueryInfo
					{
						Query = query,
						QueryTagLineNumber = ((IXmlLineInfo)sample).LineNumber - 1
					};
				}
				return null;	
			}catch(XmlException)
			{
				return null;
			}
		}

		public static IDictionary<int, FileLineReference> BuildQueryLinesToFileLines(
			int queryTagLineNumber,
			string[] queryLines,
			IList<ITextSnapshotLine> snapshotLines)
		{
			if (queryLines == null) throw new ArgumentNullException("queryLines");


			for (int i = queryTagLineNumber; i < queryTagLineNumber + queryLines.Count() + 5; i++)
			{
				if (snapshotLines[i].GetText().Contains(queryLines[0]))
				{
					return Enumerable.Range(0, queryLines.Length)
						.ToDictionary(index => index,
									index => new FileLineReference
									{
										Line = snapshotLines[i + index],
										StartPosition = snapshotLines[i + index].GetText().IndexOf(queryLines[index])
									});
				}
			}
			return new Dictionary<int, FileLineReference>();
		}



		public class FileLineReference
		{
			public ITextSnapshotLine Line { get; set; }
			public bool ExactlyMatch { get { return StartPosition == 0; } }
			public int StartPosition { get; set; }
		}

		public class QueryInfo
		{
			public string Query { get; set; }
			public int QueryTagLineNumber { get; set; }
		}

		public static string GetQueryByName(this ITextSnapshot snapshot, string name)
		{
			XDocument document = XDocument.Parse(string.Join(Environment.NewLine, snapshot.Lines.Select(l => l.GetText())));
			var query = document.DescendantNodes()
							.OfType<XElement>()
							.Where(e => e.Name.LocalName == "query" && e.Attributes().Any(a => a.Name == "name" && a.Value == name))
							.FirstOrDefault();
			return query != null ? query.Value : string.Empty;
		}

		public static bool IsInQueryTag(this SnapshotSpan span)
		{
			var lines = span.Snapshot.Lines.Select(l => l.GetText()).ToArray();
			var currentLineNumber = span.Start.GetContainingLine().LineNumber;
			var foundOpenQueryTag = false;
			var foundClosedComment = false;
			for (var i = currentLineNumber; i >= 0; i--)
			{
				if (lines[i].Trim().Contains("<query")) foundOpenQueryTag = true;
				if (lines[i].Contains("-->")) foundClosedComment = true;
				if (!foundClosedComment && lines[i].Trim().Contains("<!--")) return false;
				if (!foundOpenQueryTag && lines[i].Trim().Contains("</query")) return false;
			}
			return foundOpenQueryTag;
		}

		public static bool IsInQueryTag(this SnapshotPoint span)
		{
			var lines = span.Snapshot.Lines.Select(l => l.GetText()).ToArray();
			var currentLineNumber = span.GetContainingLine().LineNumber;
			var foundOpenQueryTag = false;
			var foundClosedComment = false;
			for (var i = currentLineNumber; i >= 0; i--)
			{
				if (lines[i].Trim().Contains("<query")) foundOpenQueryTag = true;
				if (lines[i].Contains("-->")) foundClosedComment = true;
				if (!foundClosedComment && lines[i].Trim().Contains("<!--")) return false;
				if (!foundOpenQueryTag && lines[i].Trim().Contains("</query")) return false;
			}
			return foundOpenQueryTag;
		}
	}
}